/*
 * $Id: CategoryUtils.java 26 2012-01-19 21:59:06Z rook.alex@gmail.com $
 */
package ua.org.energy.vkek.site.server;

import com.google.appengine.api.datastore.Key;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import ua.org.energy.vkek.site.model.ArticleCategory;
import ua.org.energy.vkek.site.model.ArticleTag;

//TODO: возращать локализованный текст ошибок
//-  ломается кодировка при передаче через сеть
public class CategoryUtils {

    private EntityManager em;

    public CategoryUtils(EntityManager em) {
        this.em = em;
    }

    public ArticleCategory insert(ArticleCategory category) throws DataIOException {
        healtCheck(category);
        checkUnique(category.getCategoryName());
        try {
            em.persist(category);
            return category;
        } catch (Exception e) {
            throw new DataIOException(e.getMessage());
        }

    }

    public ArticleCategory insert(String categoryName) throws DataIOException {
        ArticleCategory ArticleCategory = new ArticleCategory();
        ArticleCategory.setCategoryName(categoryName);
        return insert(ArticleCategory);
    }

    public ArticleCategory update(String oldCategoryName, String newCategoryName) throws DataIOException {
        checkUnique(newCategoryName);
        ArticleCategory oldval = findCategory(oldCategoryName);
        if (oldval != null) {
            oldval.setCategoryName(newCategoryName);
            return update(oldval);
        } else {
            throw new DataIOException("could not find Category for update");
        }
    }

    /*
     * Нет возможности для этого метода делать checkUnique поскольку
     * findCategory(String categoryName) неявно убирает dirty-флаги c полей
     * объекта и при вызове merge возникает ошибка
     * org.datanucleus.exceptions.NucleusException: Object is marked as dirty
     * yet no fields are marked dirty
     */
    private ArticleCategory update(ArticleCategory category) throws DataIOException {
        healtCheck(category);
        if (category.getIdent() != null) { //категория имеет ключ следовательно уже есть/была в базе
            try {
                em.merge(category);
                return category;
            } catch (Exception e) {
                throw new DataIOException(e.getMessage());
            }
        } else {
            //If X is a new entity instance, a new managed entity instance X' is created and the state of X is copied into the new managed entity instance X'.
            //см. JSR 220: Enterprise JavaBeansTM,Version 3.0  Java Persistence API раз. 3.2.4.1
            throw new DataIOException("non existent Category could not be updated");
        }
    }

    public void delete(String categoryName) throws DataIOException {
        //TODO: проверить нет ли удаления по каскаду, если есть - предотварить
        try {
            ArticleCategory oldval = findCategory(categoryName);
            if (oldval != null) {
                if (hasTags(oldval)) {
                    throw new DataIOException("this category has own one or more tags, remove them first");
                }
                em.remove(oldval);
            } else {
                throw new DataIOException("could not find Category for delete");
            }
        } catch (Exception e) {
            throw new DataIOException(e.getMessage());
        }
    }

    public boolean hasTags(ArticleCategory category) {
        List<ArticleTag> orphaned = findTags(category);
        return (!orphaned.isEmpty());
    }

    public List<ArticleTag> findTags(ArticleCategory category) {
        return (List<ArticleTag>) em.createNamedQuery("ArticleCategory.findTagsByCategory").setParameter("category", category).getResultList();
    }

    public static ArticleCategory findCategory(String categoryName, EntityManager em) {
        try {
            Object obj = em.createNamedQuery("ArticleCategory.findByName").setParameter("catName", categoryName).getSingleResult();
            return (ArticleCategory) obj;
        } catch (NoResultException e) {
            return null;
        }
    }

    public ArticleCategory findCategory(Key key) {
        return em.find(ArticleCategory.class, key);
    }

    public ArticleCategory findCategory(String categoryName) {
        return findCategory(categoryName, this.em);
    }

    public List<ArticleCategory> listAll() {
        return (List<ArticleCategory>) em.createNamedQuery("ArticleCategory.findAll").getResultList();
    }

    private void healtCheck(ArticleCategory category) throws DataIOException {
        if ((category == null) || (category.getCategoryName() == null)) {
            throw new DataIOException("Category can't be a null");
        }
    }

    private void checkUnique(String categoryName) throws DataIOException {
        /*
         * why not @Column(unique = true) on categoryName ? , - because
         * java.lang.UnsupportedOperationException: No support for uniqueness
         * constraints
         */
        if (findCategory(categoryName) != null) {
            throw new DataIOException("Category with given name already exist");
        }

    }
}
